#include <stdint.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

#include "circ_buf.h"
#include "shmfifo.h"

void * __l0032_shm_initialize(const char* shmname, size_t sz, int iscreated);

struct l0032_shmfifo* l0032_shmfifo_open(const char* shmname, size_t blobsz, size_t nblobs, int bCreate)
{	
	struct l0032_shmfifo* shmfifo = NULL;
	//int iscreated = 0;

//	ERRSYS_FATALPRINT("%s: create shmstruct lockfile %s\n", __func__, lockfile);
	if (shmname && strlen(shmname) < L0032_SHM_FILENAME_MAX && L0032_ISPOW2(nblobs)) {
		size_t sz = blobsz * nblobs + sizeof(struct l0032_shmfifo);
		sz = L0032_PAGEALIGN(sz);

		shmfifo = __l0032_shm_initialize(shmname, sz, bCreate);
		if (shmfifo && bCreate) {
			shmfifo->sz = sz;
			shmfifo->nblob = nblobs;
			shmfifo->blobsz = blobsz;

			//printf("---create shmname:%s, sz:%lu, nblob:%d, blobsz:%d \n", shmname, shmfifo->sz, shmfifo->nblob, shmfifo->blobsz);
		}else{
			//printf("---open shmname:%s, sz:%lu, nblob:%d, blobsz:%d \n", shmname, shmfifo->sz, shmfifo->nblob, shmfifo->blobsz);
		}
	}
	else if (shmname == NULL) {
		printf("Shm name is NULL\n");
		goto err1;
	}
	else if (strlen(shmname) >= L0032_SHM_FILENAME_MAX) {
		//printf("Shm name is length is too big(%ld)\n\n", strlen(shmname));
		goto err1;
	}
	else if (!L0032_ISPOW2(nblobs)) {
		//printf("Invalid number of nblobs (%ld)\n", nblobs);
		goto err1;
	}
	//printf("l0032_shmfifo_open shmfifo->sz:%ld \n", shmfifo->sz);

err1:
	return shmfifo;

}

int l0032_shmfifo_put(void* tshmfifo, uint8_t *blob, uint32_t blobsz)
{
	struct l0032_shmfifo* shmfifo = (struct l0032_shmfifo *)tshmfifo;
	if (shmfifo != NULL && CIRC_SPACE(shmfifo->head, shmfifo->tail, shmfifo->nblob)) {
		uint8_t *shmfifo_blob = shmfifo->blob + shmfifo->head * shmfifo->blobsz;
		blobsz = blobsz >= shmfifo->blobsz ? shmfifo->blobsz : blobsz;
		memcpy(shmfifo_blob, blob, blobsz);
		shmfifo->head ++;
		shmfifo->head &= shmfifo->nblob - 1;
		return 0;
	}

	return -1;
}

int l0032_shmfifo_get(void* tshmfifo, uint8_t *blob, uint32_t blobsz)
{
	struct l0032_shmfifo* shmfifo = (struct l0032_shmfifo *)tshmfifo;
	if (shmfifo != NULL && CIRC_CNT(shmfifo->head, shmfifo->tail, shmfifo->nblob) != 0) {
		uint8_t *shmfifo_blob = shmfifo->blob + shmfifo->tail * shmfifo->blobsz;
		blobsz = blobsz >= shmfifo->blobsz ? shmfifo->blobsz : blobsz;
		memcpy(blob, shmfifo_blob, blobsz);
		shmfifo->tail ++;
		shmfifo->tail &= shmfifo->nblob - 1;
		return 0;
	}
	
	return -1;
}

int l0032_shmfifo_cnt(void* tshmfifo)
{
	struct l0032_shmfifo* shmfifo = (struct l0032_shmfifo *)tshmfifo;
	
	if( shmfifo == NULL){
		return -1;
	}else{
		return CIRC_CNT(shmfifo->head, shmfifo->tail, shmfifo->nblob);
	}
}

int l0032_shmfifo_space(void* tshmfifo)
{
	struct l0032_shmfifo* shmfifo = (struct l0032_shmfifo *)tshmfifo;
	if( shmfifo == NULL){
		return -1;
	}else{
		return CIRC_SPACE(shmfifo->head, shmfifo->tail, shmfifo->nblob);
	} 
}


uint32_t l0032_shmfifo_drain(struct l0032_shmfifo* shmfifo, uint8_t *mem, uint32_t sz)
{
	uint32_t count = 0;
	uint32_t n = 0;
	if (shmfifo != NULL && (count = CIRC_CNT(shmfifo->head, shmfifo->tail, shmfifo->nblob)) != 0) {
		while (count -- && sz >= shmfifo->blobsz) {
			uint8_t *shmfifo_blob = shmfifo->blob + shmfifo->tail * shmfifo->blobsz;
			if (sz > shmfifo->blobsz) {
				memcpy(mem, shmfifo_blob, sz);
				shmfifo->tail ++;
				shmfifo->tail &= shmfifo->nblob - 1;
				sz -= shmfifo->blobsz;
				mem += shmfifo->blobsz;
				n ++;
			}
		}
	}
	
	return n;
}

void l0032_shmfifo_flush(struct l0032_shmfifo* shmfifo)
{
	if (shmfifo != NULL && CIRC_CNT(shmfifo->head, shmfifo->tail, shmfifo->nblob) != 0) {
		shmfifo->tail = shmfifo->head;
	}
}

void l0032_shmfifo_close(struct l0032_shmfifo* shmfifo)
{
	if (shmfifo) {
		munmap(shmfifo, shmfifo->sz);	
	}
}


